/**
 * File name: DotFileReader.java Author: Willard Wang Create date: May 22, 2013
 */
package edu.sjtu.se.prophet.io;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import edu.sjtu.se.prophet.exception.EdgeExistsException;
import edu.sjtu.se.prophet.exception.InvalidDataSourceException;
import edu.sjtu.se.prophet.exception.NodeExistsException;
import edu.sjtu.se.prophet.exception.NodeNotFoundException;
import edu.sjtu.se.prophet.graph.Graph;

/**
 * Read a dot file into a {@link edu.sjtu.se.prophet.graph.Graph}. A dot file
 * example:
 * 
 * <pre>
 * digraph "Call Graph" {
 * 	label="Call Graph";
 * 	Node0x874de0 [shape=record,label="{A()}"];
 * 	Node0x874de0 -> Node0x875290;
 * 	Node0x874de0 -> Node0x875330;
 * 	Node0x875290 [shape=record,label="{B()}"];
 * 	Node0x875290 -> Node0x8752c0;
 * 	Node0x875330 [shape=record,label="{C()}"];
 * 	Node0x875330 -> Node0x8752c0;
 * 	Node0x8752c0 [shape=record,label="{D()}"];
 * 	Node0x872c50 [shape=record,label="{main}"];
 * 	Node0x872c50 -> Node0x874de0;
 * }
 * </pre>
 * 
 * @author deathspeeder
 * 
 */
public class DotFileReader implements GraphReader {
    class Edge {
        public String from;
        public String to;

        public Edge(String f, String t) {
            from = f;
            to = t;
        }
    }

    private List<String> uris = new ArrayList<String>();
    private Map<String, String> nodeDefinitions;
    private List<Edge> edgeDefinitions;
    private Graph graph;

    /*
     * (non-Javadoc)
     * 
     * @see edu.sjtu.se.prophet.io.GraphReader#addSource(java.lang.String)
     */
    @Override
    public void addSource(String uri) {
        uris.add(uri);
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.sjtu.se.prophet.io.GraphReader#read()
     */
    @Override
    public List<Graph> read() throws IOException, InvalidDataSourceException {
        graph = new Graph();

        // read all files first
        nodeDefinitions = new HashMap<String, String>();
        edgeDefinitions = new ArrayList<Edge>();
        Iterator<String> it = uris.iterator();
        while (it.hasNext()) {
            read(it.next());
        }

        // insert nodes
        for (String func : nodeDefinitions.values()) {
            try {
                graph.addNode(func);
            } catch (NodeExistsException e) {
                throw new InvalidDataSourceException("Node exists: " + func, e);
            }
        }

        // add edges
        for (Edge edge : edgeDefinitions) {
            String fromFunc = nodeDefinitions.get(edge.from);
            String toFunc = nodeDefinitions.get(edge.to);

            if (fromFunc == null)
                throw new InvalidDataSourceException("Node not defined: "
                        + edge.from);
            if (toFunc == null)
                throw new InvalidDataSourceException("Node not defined: "
                        + edge.to);

            try {
                graph.addEdge(fromFunc, toFunc);
            } catch (NodeNotFoundException e) {
                throw new InvalidDataSourceException("Node not found", e);
            } catch (EdgeExistsException e) {
                throw new InvalidDataSourceException("Edge exists", e);
            }
        }

        return null; // TODO
    }

    /**
     * @throws IOException
     */
    private void read(String path) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(
                new FileInputStream(path)));
        String line = null;
        do {
            line = br.readLine();
            parse(line);
        } while (null != line);
        br.close();
    }

    /**
     * @param line
     */
    private void parse(String line) {
        if (null != line) {
            Pattern nodePattern = Pattern.compile("Node0x[0-9]+");
            Pattern funcPattern = Pattern.compile("\\{.*\\}");
            Matcher nodeMatcher = nodePattern.matcher(line);
            Matcher funcMatcher = funcPattern.matcher(line);
            if (funcMatcher.find()) {
                String temp = funcMatcher.group();
                String funcName = temp.substring(1, temp.length() - 1);
                nodeDefinitions.put(nodeMatcher.group(), funcName);
            } else {
                edgeDefinitions.add(new Edge(nodeMatcher.group(0), nodeMatcher
                        .group(1)));
            }
        }
    }

    /* (non-Javadoc)
     * @see edu.sjtu.se.prophet.io.SettableIO#setValue(java.lang.String, java.lang.String)
     */
    @Override
    public void setValue(String key, String value) {
        // TODO Auto-generated method stub
        
    }

}
